import ctypes
import threading
import sys
from time import sleep

DLLPATH = r'./libs/USB2DaqsA_x64.dll'
#DLLPATH = r'D:\programs\blanking_count_control\libs\USB2DaqsA_x64.dll'
AD7606DLL = ctypes.windll.LoadLibrary(DLLPATH)  # Load the DLL


class ADC_CONFIG(ctypes.Structure):
    """ADC Configuration Structure"""
    _fields_ = [
        ("byADCOptions", ctypes.c_ubyte),
        ("byTrigOptions", ctypes.c_ubyte),
        ("wReserved1", ctypes.c_ushort),
        ("byMainCh", ctypes.c_ubyte),
        ("byUnused", ctypes.c_ubyte),
        ("wReserved2", ctypes.c_ushort),
        ("wPeriod", ctypes.c_ushort),
        ("wTrigVol", ctypes.c_ushort),
        ("dwCycleCnt", ctypes.c_ulong),
        ("dwMaxCycles", ctypes.c_ulong)
    ]


class AD7606Win:
    def __init__(self):
        self.device_index = 255
        self.adc_config = ADC_CONFIG()
        self.adc_started = False
        self.data_list = []  # 通道一的数据
        self.setup_functions()

    def setup_functions(self):
        """Set argument and return types for DLL functions."""
        AD7606DLL.M3F20xm_GetSerialNo.argtypes = [ctypes.c_ubyte, ctypes.c_char_p]
        AD7606DLL.M3F20xm_GetSerialNo.restype = ctypes.c_ubyte

        AD7606DLL.M3F20xm_OpenDevice.restype = ctypes.c_ubyte
        AD7606DLL.M3F20xm_OpenDeviceByNumber.argtypes = [ctypes.c_char_p]
        AD7606DLL.M3F20xm_OpenDeviceByNumber.restype = ctypes.c_ubyte

        AD7606DLL.M3F20xm_GetVersion.argtypes = [ctypes.c_ubyte, ctypes.c_ubyte, ctypes.c_char_p]
        AD7606DLL.M3F20xm_GetVersion.restype = ctypes.c_bool

        AD7606DLL.M3F20xm_CloseDevice.argtypes = [ctypes.c_ubyte]
        AD7606DLL.M3F20xm_CloseDevice.restype = ctypes.c_bool

        AD7606DLL.M3F20xm_ADCGetConfig.argtypes = [ctypes.c_ubyte, ctypes.POINTER(ADC_CONFIG)]
        AD7606DLL.M3F20xm_ADCGetConfig.restype = ctypes.c_bool

        AD7606DLL.M3F20xm_ADCSetConfig.argtypes = [ctypes.c_ubyte, ctypes.POINTER(ADC_CONFIG)]
        AD7606DLL.M3F20xm_ADCSetConfig.restype = ctypes.c_bool

        AD7606DLL.M3F20xm_ADCRead.argtypes = [ctypes.c_ubyte, ctypes.POINTER(ctypes.c_ushort)]
        AD7606DLL.M3F20xm_ADCRead.restype = ctypes.c_bool

        AD7606DLL.M3F20xm_ADCStart.argtypes = [ctypes.c_ubyte]
        AD7606DLL.M3F20xm_ADCStart.restype = ctypes.c_bool

        AD7606DLL.M3F20xm_ADCStop.argtypes = [ctypes.c_ubyte]
        AD7606DLL.M3F20xm_ADCStop.restype = ctypes.c_bool

        AD7606DLL.M3F20xm_Verify.argtypes = [ctypes.c_ubyte]
        AD7606DLL.M3F20xm_Verify.restype = ctypes.c_bool

    def open_device(self):
        """Open a device and set the device index."""
        sleep(1)
        self.device_index = AD7606DLL.M3F20xm_OpenDevice()
        if self.device_index == 255:
            print("No AD7606 device found.")
            sys.exit(0)
        print(f"Device opened with index: {self.device_index}")

    def get_serial_number(self):
        """Retrieve the serial number of the device."""
        serial_buffer = ctypes.create_string_buffer(10)
        AD7606DLL.M3F20xm_GetSerialNo(ctypes.c_ubyte(self.device_index), serial_buffer)
        serial_no = serial_buffer.value.decode('utf-8')
        print(f"Device Serial Number: {serial_no}")
        return serial_no

    def verify_device(self):
        """Authorize the device."""
        result = AD7606DLL.M3F20xm_Verify(ctypes.c_ubyte(self.device_index))
        print(f"Authorization Result: {result}")
        return result

    def get_version(self):
        """Retrieve version information."""
        version_buffer = ctypes.create_string_buffer(50)
        for type_code, description in zip([0, 1, 2], ["Library", "Driver", "Firmware"]):
            AD7606DLL.M3F20xm_GetVersion(ctypes.c_ubyte(self.device_index), ctypes.c_ubyte(type_code), version_buffer)
            version = version_buffer.value.decode('utf-8')
            print(f"{description} Version: {version}")

    def configure_adc(self):
        """Set ADC configuration."""
        self.adc_config.byADCOptions = 0b00010000
        self.adc_config.byTrigOptions = 0b10001001
        self.adc_config.wPeriod = 100  # 10k
        self.adc_config.dwMaxCycles = 0  # 无限采
        success = AD7606DLL.M3F20xm_ADCSetConfig(ctypes.c_ubyte(self.device_index), ctypes.byref(self.adc_config))
        print(f"ADC configuration successful: {success}")

    def start_adc(self):
        """Start ADC sampling."""
        self.adc_started = AD7606DLL.M3F20xm_ADCStart(ctypes.c_ubyte(self.device_index))
        print(f"ADC started: {self.adc_started}")
        if self.adc_started:
            self.data_list.clear()
            self._start_reading()

    def stop_adc(self):
        """Stop ADC sampling."""
        self.adc_started = False
        stopped = AD7606DLL.M3F20xm_ADCStop(ctypes.c_ubyte(self.device_index))
        print(f"ADC stopped: {stopped}")

    def _start_reading(self):
        """Start a separate thread to read ADC data."""
        threading.Thread(target=self._read_adc_data, daemon=True).start()

    def _read_adc_data(self):
        """Read ADC data periodically."""
        while self.adc_started:
            data_buffer = (ctypes.c_ushort * 8)()  # 8个通道
            success = AD7606DLL.M3F20xm_ADCRead(ctypes.c_ubyte(self.device_index), data_buffer)
            if success:
                channel_one_data = data_buffer[0]  # 取通道一的数据
                self.data_list.append(channel_one_data)
                print(f"Channel 1 Data: {channel_one_data}") # 日后需要注释
            sleep(0.1)  # 每0.1秒读取一次数据


if __name__ == "__main__":
    adc = AD7606Win()
    adc.open_device()
    #
    adc.get_serial_number()
    adc.verify_device()
    adc.get_version()
    #
    adc.configure_adc()
    adc.start_adc()
    sleep(5)  # 读取数据5秒
    adc.stop_adc()
